#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
构建脚本 - 用于将简易音频编辑器打包为可执行文件
交互式界面，支持Windows、macOS和Linux系统
"""

import os
import sys
import json
import shutil
import platform
import subprocess

# 获取当前系统架构信息
def get_current_architecture():
    """获取当前系统的架构信息"""
    machine = platform.machine().lower()
    
    # 标准化架构名称
    if machine in ['x86_64', 'amd64']:
        return 'x86_64'
    elif machine in ['arm64', 'aarch64']:
        return 'arm64'
    elif machine in ['armv7', 'armv7l']:
        return 'armv7'
    elif machine in ['i386', 'i686']:
        return 'i386'
    else:
        return machine

def get_architecture_choices():
    """获取可用的架构选择"""
    system = platform.system()
    current_arch = get_current_architecture()
    
    choices = []
    
    if system == "Darwin":  # macOS
        choices = [
            ("universal2", f"通用二进制 (支持Intel和Apple Silicon)"),
            ("x86_64", f"Intel x86_64"),
            ("arm64", f"Apple Silicon (M1/M2/M3)")
        ]
    elif system == "Windows":
        choices = [
            ("x86_64", f"64位系统 (x86_64)"),
            ("x86", f"32位系统 (x86)"),
            ("arm64", f"ARM64系统")
        ]
    else:  # Linux和其他系统
        choices = [
            ("x86_64", f"x86_64架构"),
            ("aarch64", f"ARM64架构"),
            ("armv7", f"ARMv7架构")
        ]
    
    return choices, current_arch

# 从JSON文件加载应用信息
def load_app_info():
    """从app_info.json文件加载应用信息"""
    try:
        with open('app_info.json', 'r', encoding='utf-8') as f:
            app_info = json.load(f)
            # 确保所有必要字段都存在
            required_fields = ["name", "version", "author", "description", "main_script"]
            for field in required_fields:
                if field not in app_info:
                    print(f"警告: app_info.json中缺少'{field}'字段")
            return app_info
    except (FileNotFoundError, json.JSONDecodeError) as e:
        print(f"错误: 无法加载应用信息 - {e}")
        print("请确保app_info.json文件存在且格式正确")
        sys.exit(1)

# 加载应用信息
APP_INFO = load_app_info()
APP_NAME = APP_INFO["name"]
APP_VERSION = APP_INFO["version"]
MAIN_SCRIPT = APP_INFO["main_script"]
AUTHOR = APP_INFO["author"]
DESCRIPTION = APP_INFO["description"]

# 获取当前系统对应的图标文件
def get_platform_icon():
    """根据操作系统获取对应的图标文件路径"""
    system = platform.system()
    
    # 优先使用新的icon_files配置
    if "icon_files" in APP_INFO and system in APP_INFO["icon_files"]:
        icon_path = APP_INFO["icon_files"][system]
        if os.path.exists(icon_path):
            return icon_path
    
    # 检查assets目录中的图标文件
    assets_icons = {
        "Windows": "assets/icon.ico",
        "Darwin": "assets/icon.icns", 
        "Linux": "assets/icon.png"
    }
    
    if system in assets_icons and os.path.exists(assets_icons[system]):
        return assets_icons[system]
    
    # 最后尝试旧的icon_file路径
    old_icon_file = "icon.ico"
    if os.path.exists(old_icon_file):
        return old_icon_file
    
    return None

def clear_screen():
    """清除终端内容"""
    os.system('cls' if platform.system() == 'Windows' else 'clear')

def print_header():
    """打印脚本标题"""
    clear_screen()
    print("=" * 60)
    print(f"{APP_NAME} 打包工具 v{APP_VERSION}".center(60))
    print("=" * 60)
    print()

# 检查必要的依赖
def check_dependencies():
    """检查必要的打包依赖是否已安装"""
    print("检查必要依赖...")
    required_packages = ["pyinstaller"]
    missing_packages = []
    
    # 使用pip list命令检查已安装的包
    try:
        pip_list = subprocess.run(
            [sys.executable, "-m", "pip", "list"], 
            stdout=subprocess.PIPE,
            text=True,
            check=True
        ).stdout.lower()
        
        for package in required_packages:
            # 检查包名是否在pip list输出中
            if package.lower() not in pip_list and package.lower().replace('-', '_') not in pip_list:
                missing_packages.append(package)
    except subprocess.CalledProcessError:
        print("警告: 无法获取已安装包列表，将尝试导入测试")
        # 如果pip list命令失败，回退到导入测试
        for package in required_packages:
            try:
                # 尝试不同的大小写形式
                try:
                    __import__(package)
                except ImportError:
                    # 尝试首字母大写版本
                    try:
                        __import__(package.capitalize())
                    except ImportError:
                        missing_packages.append(package)
            except ImportError:
                missing_packages.append(package)
    
    if missing_packages:
        print("\n缺少以下必要的依赖包：")
        for package in missing_packages:
            print(f"  - {package}")
        
        install = input("\n是否立即安装这些依赖？(y/n/exit): ").strip().lower()
        if install == 'q' or install == 'exit':
            print("已取消构建。")
            sys.exit(0)
        elif install == 'y':
            try:
                subprocess.run([sys.executable, "-m", "pip", "install"] + missing_packages, check=True)
                print("✅ 依赖安装成功！")
                return True
            except subprocess.CalledProcessError:
                print("❌ 依赖安装失败，请手动安装后重试。")
                return False
        else:
            print("请安装必要依赖后再运行此脚本。")
            return False
    
    print("✅ 所有必要依赖已安装")
    return True

# 检查FFmpeg是否安装
def check_ffmpeg():
    """检查系统是否已安装FFmpeg"""
    print("检查FFmpeg安装状态...")
    try:
        result = subprocess.run(
            ["ffmpeg", "-version"], 
            stdout=subprocess.PIPE, 
            stderr=subprocess.PIPE,
            text=True
        )
        if result.returncode == 0:
            print("✅ 检测到FFmpeg已安装")
            return True
        else:
            print("❌ 未检测到FFmpeg")
            return False
    except FileNotFoundError:
        print("❌ 未检测到FFmpeg")
        return False

# 清理之前的构建
def clean_build_dirs():
    """清理之前的构建目录"""
    print("清理之前的构建文件...")
    dirs_to_clean = ["build", "dist"]
    for dir_name in dirs_to_clean:
        if os.path.exists(dir_name):
            print(f"  - 清理 {dir_name} 目录...")
            shutil.rmtree(dir_name)
    
    # 清理.spec文件
    spec_file = f"{APP_NAME}.spec"
    if os.path.exists(spec_file):
        print(f"  - 删除 {spec_file} 文件...")
        os.remove(spec_file)
    
    # 清理 macOS 特定的 spec 文件
    if os.path.exists("macos_app.spec"):
        print("  - 删除 macos_app.spec 文件...")
        os.remove("macos_app.spec")
    
    print("✅ 清理完成")

# 构建应用
def build_app(one_file=False, console=False, with_ffmpeg=False, target_arch=None):
    """
    使用PyInstaller构建应用
    
    参数:
        one_file: 是否打包为单个文件
        console: 是否显示控制台窗口
        with_ffmpeg: 是否打包FFmpeg
        target_arch: 目标架构 (如: x86_64, arm64, universal2)
    """
    system = platform.system()
    
    # 获取当前系统对应的图标文件
    current_icon = get_platform_icon()
    
    # 基本命令参数
    cmd = [
        sys.executable,
        "-m",
        "PyInstaller",
        "--name", APP_NAME,
        "--noconfirm",
    ]
    
    # 为 macOS 使用自定义 spec 文件来正确设置版本信息
    use_custom_spec = False
    if system == "Darwin":  # macOS
        spec_template_path = "macos_app.spec.template"
        if os.path.exists(spec_template_path):
            # 生成自定义 spec 文件
            spec_file = generate_macos_spec_file(one_file, console, current_icon, target_arch)
            
            # 使用自定义 spec 文件进行构建（spec文件已包含所有配置）
            cmd = [sys.executable, "-m", "PyInstaller", spec_file, "--noconfirm"]
            use_custom_spec = True
            print(f"✅ 使用自定义 macOS spec 文件: {spec_file}")
        else:
            # 回退到基本参数
            cmd.extend([
                "--osx-bundle-identifier", f"com.audioeditor.{APP_NAME.lower().replace(' ', '')}",
            ])
    
    # 如果有图标文件，且未使用自定义spec文件
    if current_icon and not use_custom_spec:
        cmd.extend(["--icon", current_icon])
        print(f"✅ 使用图标文件: {current_icon}")
    elif not use_custom_spec:
        print("⚠️ 未找到合适的图标文件，将使用PyInstaller默认图标")
    
    # 如果未使用自定义 spec 文件，添加常规参数
    if not use_custom_spec:
        # 是否打包为单个文件
        if one_file:
            cmd.append("--onefile")
        else:
            cmd.append("--onedir")
            # 在文件夹模式下，需要将图标文件作为数据文件包含进去
            if current_icon:
                separator = ";" if system == "Windows" else ":"
                cmd.append(f"--add-data={current_icon}{separator}.")
                print(f"✅ 已将图标文件添加到数据文件: {current_icon}")
        
        # 是否显示控制台
        if not console:
            if system == "Windows":
                cmd.append("--noconsole")
            elif system == "Darwin":  # macOS
                cmd.append("--windowed")
        
        # 架构相关参数
        if target_arch and system == "Darwin":  # macOS支持架构指定
            if target_arch == "universal2":
                cmd.append("--target-arch=universal2")
            elif target_arch == "x86_64":
                cmd.append("--target-arch=x86_64")
            elif target_arch == "arm64":
                cmd.append("--target-arch=arm64")
        
        # 添加需要包含的文件和目录
        data_includes = []
        
        # 包含本地化翻译文件
        locales_dir = os.path.join("src", "locales")
        if os.path.exists(locales_dir) and os.path.isdir(locales_dir):
            separator = ";" if system == "Windows" else ":"
            # 无论是单文件还是目录模式，都保持相同的目标路径
            # 这样language.py的处理逻辑可以保持一致
            target_path = os.path.join("src", "locales")
            cmd.append(f"--add-data={locales_dir}{separator}{target_path}")
            print(f"✅ 已添加本地化文件: {locales_dir} -> {target_path}")
        
        # 如果选择打包FFmpeg
        if with_ffmpeg:
            # 在这里需要处理FFmpeg的打包逻辑
            ffmpeg_path = find_ffmpeg()
            if ffmpeg_path:
                ffmpeg_dir = os.path.dirname(ffmpeg_path)
                if system == "Windows":
                    ffmpeg_files = ["ffmpeg.exe", "ffprobe.exe"]
                    for file in ffmpeg_files:
                        full_path = os.path.join(ffmpeg_dir, file)
                        if os.path.exists(full_path):
                            data_includes.append(f"--add-data={full_path};.")
                else:  # macOS 和 Linux
                    data_includes.append(f"--add-data={ffmpeg_path}:.")
        
        # 添加数据文件
        cmd.extend(data_includes)
        
        # 添加主脚本
        cmd.append(MAIN_SCRIPT)
    
    # 执行构建
    print("\n开始构建应用...")
    print(f"命令: {' '.join(cmd)}")
    
    try:
        subprocess.run(cmd, check=True)
        print(f"\n✅ 构建成功！你可以在 dist 目录中找到{'可执行文件' if one_file else '应用目录'}")
        
        # 构建后的路径
        if one_file:
            if system == "Windows":
                build_path = f"dist/{APP_NAME}.exe"
            else:
                build_path = f"dist/{APP_NAME}"
        else:
            build_path = f"dist/{APP_NAME}"
        
        print(f"应用路径: {os.path.abspath(build_path)}")
    except subprocess.CalledProcessError as e:
        print(f"❌ 构建失败: {e}")
        return False
    
    return True

# 查找系统中的FFmpeg路径
def find_ffmpeg():
    """查找系统中安装的FFmpeg路径"""
    system = platform.system()
    ffmpeg_cmd = "ffmpeg.exe" if system == "Windows" else "ffmpeg"
    
    try:
        if system == "Windows":
            result = subprocess.run(
                ["where", ffmpeg_cmd], 
                stdout=subprocess.PIPE, 
                stderr=subprocess.PIPE,
                text=True
            )
        else:  # macOS 和 Linux
            result = subprocess.run(
                ["which", ffmpeg_cmd], 
                stdout=subprocess.PIPE, 
                stderr=subprocess.PIPE,
                text=True
            )
        
        if result.returncode == 0:
            return result.stdout.strip()
    except:
        pass
    
    return None

# 生成macOS spec文件
def generate_macos_spec_file(one_file=False, console=False, icon_path=None, target_arch=None, bundle_id=None):
    """生成macOS应用的spec文件，包含完整的版本信息"""
    
    # 读取spec模板
    with open('macos_app.spec.template', 'r', encoding='utf-8') as f:
        template = f.read()
    
    # 替换模板变量
    spec_content = template.replace('{{APP_NAME}}', APP_NAME)
    spec_content = spec_content.replace('{{APP_VERSION}}', APP_VERSION)
    spec_content = spec_content.replace('{{APP_AUTHOR}}', AUTHOR)
    spec_content = spec_content.replace('{{APP_BUNDLE_ID}}', APP_INFO["macos_bundle_id"])
    spec_content = spec_content.replace('{{ICON_PATH}}', icon_path if icon_path else '')
    spec_content = spec_content.replace('{{CONSOLE}}', 'True' if console else 'False')
    spec_content = spec_content.replace('{{ONE_FILE}}', 'True' if one_file else 'False')
    
    # 生成spec文件名并写入（使用英文名称避免Python执行问题）
    spec_filename = "macos_app.spec"
    with open(spec_filename, 'w', encoding='utf-8') as f:
        f.write(spec_content)
    
    return spec_filename

# 交互式界面
def interactive_build():
    """交互式界面让用户选择构建选项"""
    print_header()
    
    # 显示应用信息
    print(f"应用名称: {APP_NAME}")
    print(f"版本: {APP_VERSION}")
    print(f"作者: {AUTHOR}")
    print(f"描述: {DESCRIPTION}")
    print(f"主脚本: {MAIN_SCRIPT}")
    
    # 获取当前系统对应的图标
    current_icon = get_platform_icon()
    if current_icon:
        print(f"图标文件: {current_icon}")
    else:
        print("图标文件: 未找到合适的图标文件，将使用PyInstaller默认图标")
    
    # 检查依赖
    if not check_dependencies():
        input("\n按Enter键退出...")
        return
    
    # 添加退出选项提示
    print("\n💡 提示：在任何步骤中输入 'q' 或 'exit' 可随时退出构建流程")
    
    # 检查FFmpeg
    ffmpeg_installed = check_ffmpeg()
    
    print("\n构建选项配置")
    print("-" * 40)
    
    # 获取架构信息
    arch_choices, current_arch = get_architecture_choices()
    
    # 选择目标架构
    print(f"\n1. 目标架构 (当前设备: {current_arch}):")
    for i, (arch_code, arch_desc) in enumerate(arch_choices, 1):
        current_marker = " [当前设备]" if arch_code == current_arch else ""
        print(f"   [{i}] {arch_desc}{current_marker}")
    
    while True:
        arch_choice = input(f"   选择目标架构 (1-{len(arch_choices)}) [默认: {current_arch}]: ").strip()
        if arch_choice.lower() in ['q', 'exit']:
            print("已取消构建。")
            sys.exit(0)
        if arch_choice == '':
            target_arch = current_arch
            break
        try:
            arch_index = int(arch_choice) - 1
            if 0 <= arch_index < len(arch_choices):
                target_arch = arch_choices[arch_index][0]
                break
        except ValueError:
            pass
        print("   无效选择，请重试。")
    
    # 选择打包模式
    print("\n2. 打包模式:")
    print("   [1] 单个可执行文件 (体积较大但方便分发)")
    print("   [2] 文件夹 (推荐，体积较小但需要保持文件结构完整)")
    
    while True:
        mode_choice = input("   选择打包模式 (1/2/exit) [默认: 2]: ").strip()
        if mode_choice.lower() in ['q', 'exit']:
            print("已取消构建。")
            sys.exit(0)
        if mode_choice == '':
            mode_choice = '2'  # 默认选择文件夹模式
        if mode_choice in ['1', '2']:
            break
        print("   无效选择，请重试。")
    
    one_file = mode_choice == '1'
    
    # 是否显示控制台
    print("\n3. 控制台窗口:")
    print("   [1] 隐藏控制台 (推荐，正常用户使用)")
    print("   [2] 显示控制台 (用于调试，可以查看输出和错误)")
    
    while True:
        console_choice = input("   选择控制台选项 (1/2/exit): ").strip()
        if console_choice.lower() in ['q', 'exit']:
            print("已取消构建。")
            sys.exit(0)
        if console_choice in ['1', '2']:
            break
        print("   无效选择，请重试。")
    
    console = console_choice == '2'
    
    # 是否打包FFmpeg
    with_ffmpeg = False
    if ffmpeg_installed:
        print("\n4. FFmpeg打包:")
        print("   [1] 不打包FFmpeg (推荐，要求用户自行安装FFmpeg)")
        print("   [2] 打包FFmpeg (应用体积会显著增大)")
        
        while True:
            ffmpeg_choice = input("   选择FFmpeg选项 (1/2/exit): ").strip()
            if ffmpeg_choice.lower() in ['q', 'exit']:
                print("已取消构建。")
                sys.exit(0)
            if ffmpeg_choice in ['1', '2']:
                break
            print("   无效选择，请重试。")
        
        with_ffmpeg = ffmpeg_choice == '2'
    else:
        print("\n❗ 未检测到FFmpeg，无法打包FFmpeg到应用中")
        print("  用户需要自行安装FFmpeg才能使用此应用的完整功能")
    
    # 确认选项
    print("\n已选择的构建选项:")
    print(f"  - 目标架构: {target_arch}")
    print(f"  - {'单个可执行文件' if one_file else '文件夹'}")
    print(f"  - {'显示' if console else '隐藏'}控制台")
    print(f"  - {'打包' if with_ffmpeg else '不打包'}FFmpeg")
    
    confirm = input("\n确认开始构建? (y/n/exit): ").strip().lower()
    if confirm in ['q', 'exit']:
        print("已取消构建。")
        sys.exit(0)
    elif confirm != 'y':
        print("已取消构建。")
        input("\n按Enter键退出...")
        return
    
    # 清理之前的构建
    clean_build_dirs()
    
    # 构建应用
    success = build_app(
        one_file=one_file,
        console=console,
        with_ffmpeg=with_ffmpeg,
        target_arch=target_arch
    )
    
    if success:
        print("\n提示：")
        if not with_ffmpeg:
            print("1. 此应用需要用户系统已安装FFmpeg才能正常工作")
            print("2. 在分发应用时，请确保用户已安装FFmpeg或提供安装说明")
        
        if platform.system() == "Darwin" and one_file:  # macOS单文件模式
            print("3. 在macOS上，单文件模式可能会导致某些权限问题，如遇问题请尝试目录模式")
        
        print("\n祝您使用愉快！")
    
    input("\n按Enter键退出...")

if __name__ == "__main__":
    interactive_build()